<!DOCTYPE html>
<html lang="zh-CN">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title id="pageTitle">-进度预览</title>
    <link href="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet">
    <style>
        :root {
            --node-width: 260px;
            --primary-color: #4a90e2;
            --running-gradient: linear-gradient(135deg, #ff6b6b 0%, #ff9f43 100%);
            --progress-gradient: linear-gradient(45deg, #4a90e2 25%, #6c5ce7 100%);
            --container-width: 1320px;
            --gap-base: 1rem;
        }

        body {
            font-family: 'Arial', sans-serif;
            background: linear-gradient(135deg, #f8f9fa 0%, #e9ecef 100%);
            min-height: 100vh;
            padding: 2rem 1rem;
            margin: 0;
        }

        /* 容器系统 */
        .container {
            width: 100%;
            max-width: var(--container-width);
            margin: 0 auto;
            padding: 0 1rem;
        }

        /* 标题系统 */
        .title {
            text-align: center;
            margin: 0 0 3rem;
            font-size: 2.5rem;
            font-weight: 700;
            line-height: 1.2;
            background-clip: text;
            -webkit-background-clip: text;
            color: transparent;
            background-image: linear-gradient(135deg, #4a90e2, #6c5ce7);
        }

        /* 布局系统 */
        .flex-column {
            display: flex;
            flex-direction: column;
            align-items: center;
        }

        .flex-row {
            display: flex;
            gap: var(--gap-base);
            justify-content: center;
        }

        .grid-responsive {
            display: grid;
            gap: var(--gap-base);
            grid-template-columns: repeat(auto-fit, minmax(var(--node-width), 1fr));
        }

        /* 主流程容器 */
        .main-flow {
            display: flex;
            flex-direction: column;
            align-items: center;
            position: relative;
            padding: 0 20px;
            isolation: isolate;
        }

        /* 并行分支容器 */
        .parallel-section {
            display: flex;
            justify-content: center;
            gap: 30px;
            margin: 30px 0;
            position: relative;
        }

        /* 流程元素 */
        .parallel-column {
            background: rgba(255, 255, 255, 0.9);
            border-radius: 1rem;
            padding: 1.5rem;
            box-shadow: 0 0.5rem 2rem rgba(0, 0, 0, 0.1);
            border: 1px solid rgba(255, 255, 255, 0.3);
            min-width: var(--node-width);
            backdrop-filter: blur(10px);
        }

        /* 任务节点（保留原有动效） */
        .node {
            width: var(--node-width);
            min-height: 5.625rem;
            background: rgba(255, 255, 255, 0.95);
            border-radius: 0.75rem;
            padding: 1.125rem;
            margin: 1.125rem 0;
            box-shadow: 0 0.25rem 1.25rem rgba(0, 0, 0, 0.08);
            transition: transform 0.3s ease, box-shadow 0.3s ease;
            position: relative;
            overflow: hidden;
            border: 1px solid rgba(0, 0, 0, 0.05);
        }

        .node::before {
            content: '';
            position: absolute;
            top: -50%;
            left: -50%;
            width: 200%;
            height: 200%;
            background: var(--running-gradient);
            opacity: 0;
            transition: opacity 0.3s;
            z-index: 0;
        }

        .node:hover {
            transform: translateY(-5px) scale(1.02);
            box-shadow: 0 0 30px rgba(74, 144, 226, 0.5);
        }

        /* 运行中状态特效 */
        .status-running {
            position: relative;
            animation: nodePulse 2s infinite;
        }

        .status-running::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            border-radius: 12px;
            animation: borderGlow 2s infinite;
            pointer-events: none;
        }

        /* 进度条样式 */
        .progress-container {
            position: relative;
            height: 12px;
            background: rgba(0, 0, 0, 0.05);
            border-radius: 6px;
            overflow: hidden;
            margin-top: 15px;
        }

        .progress-bar {
            height: 100%;
            background: var(--progress-gradient);
            position: relative;
            transition: width 0.5s ease-out;
        }

        .progress-bar::after {
            content: '';
            position: absolute;
            top: 0;
            left: 0;
            right: 0;
            bottom: 0;
            background: linear-gradient(90deg,
            transparent 25%,
            rgba(255, 255, 255, 0.3) 50%,
            transparent 75%);
            animation: progressShine 1.5s infinite;
        }

        .branch-connector {
            height: 2px;
            width: 60px;
            top: 50%;
        }

        /* 合并点样式 */
        .merge-point {
            width: 24px;
            height: 24px;
            background: var(--primary-color);
            border-radius: 50%;
            margin: 40px auto;
            position: relative;
            box-shadow: 0 0 20px rgba(74, 144, 226, 0.3);
            animation: mergePulse 2s infinite;
        }

        /* 动画定义 */
        @keyframes nodePulse {
            0% {
                box-shadow: 0 0 0 0 rgba(255, 107, 107, 0.4);
            }
            70% {
                box-shadow: 0 0 0 12px rgba(255, 107, 107, 0);
            }
            100% {
                box-shadow: 0 0 0 0 rgba(255, 107, 107, 0);
            }
        }

        @keyframes borderGlow {
            0% {
                box-shadow: inset 0 0 12px rgba(255, 107, 107, 0.3);
            }
            50% {
                box-shadow: inset 0 0 20px rgba(255, 107, 107, 0.5);
            }
            100% {
                box-shadow: inset 0 0 12px rgba(255, 107, 107, 0.3);
            }
        }

        @keyframes progressShine {
            0% {
                transform: translateX(-100%);
            }
            100% {
                transform: translateX(100%);
            }
        }

        @keyframes mergePulse {
            0% {
                transform: scale(0.95);
                opacity: 0.8;
            }
            50% {
                transform: scale(1.05);
                opacity: 1;
            }
            100% {
                transform: scale(0.95);
                opacity: 0.8;
            }
        }

        /* 状态标签 */
        .status-badge {
            font-size: 0.8em;
            padding: 6px 14px;
            border-radius: 20px;
            background: rgba(0, 0, 0, 0.08);
            color: #2c3e50;
            display: inline-block;
            margin-top: 10px;
            backdrop-filter: blur(4px);
            border: 1px solid rgba(0, 0, 0, 0.05);
        }

        .status-running .status-badge {
            background: rgba(255, 107, 107, 0.15);
            color: #d63031;
            animation: badgePulse 1.5s infinite;
        }

        .status-completed {
            background: rgba(120, 255, 120, 0.15);
            color: #d63031;
        }

        @keyframes badgePulse {
            0% {
                opacity: 0.8;
                transform: scale(1);
            }
            50% {
                opacity: 1;
                transform: scale(1.05);
            }
            100% {
                opacity: 0.8;
                transform: scale(1);
            }
        }

        /* 图标动画 */
        .node-icon {
            font-size: 1.2em;
            margin-right: 8px;
            display: inline-block;
        }

        .status-running .node-icon {
            animation: iconFloat 1.5s ease-in-out infinite;
        }

        @keyframes iconFloat {
            0%, 100% {
                transform: translateY(0);
            }
            50% {
                transform: translateY(-5px);
            }
        }

        /* 炫酷庆祝特效 */
        .confetti {
            position: absolute;
            width: 10px;
            height: 10px;
            background-color: #ff6b6b;
            border-radius: 50%;
            animation: confettiAnimation 2s ease-out forwards;
        }

        @keyframes confettiAnimation {
            0% {
                opacity: 1;
                transform: translateY(0) rotate(0deg);
            }
            100% {
                opacity: 0;
                transform: translateY(-100vh) rotate(360deg);
            }
        }

        /* 新增响应式设计 */
        @media (max-width: 768px) {
            :root {
                --node-width: 90%;
                --container-width: 100%;
            }

            .flex-row {
                flex-wrap: wrap;
            }

            .parallel-section {
                gap: 20px;
            }

            .node {
                margin: 12px 0;
            }
        }
    </style>
</head>

<body>
<div class="container">
    <h1 class="title">
        <span id="headerTitle"></span>
    </h1>

    <div id="progress-container" class="flex-column"></div>
</div>

<script>
    // 初始化参数
    const urlParams = new URLSearchParams(window.location.search);
    const progressId = urlParams.get('progressId');
    const activityId = urlParams.get('activityId');

    // 启动初始化
    init();

    // 触发mock
    async function mockProgress() {
        try {
            const response = await fetch(`http://localhost:8080/api/v1/activities/testProgress?progressId=_shoulderMockAndTest&activityId=MyTaskEnum`, {
                method: 'POST',
                headers: {
                    'Content-Type': 'application/json',
                }
            });
            console.log("特殊的 progressId=_shoulderMockAndTest, 进入演示模式，自动创建流程并执行！");
        } catch (error) {
            console.error('进入演示模式失败:', error, response);
        }
    }

    // 获取活动结构
    async function fetchActivityStruct() {
        try {
            const response = await fetch(`http://localhost:8080/api/v1/activities/definition?activityId=${activityId}`);
            return await response.json();
        } catch (error) {
            console.error('获取活动结构失败:', error);
            alert('系统初始化失败，请刷新页面重试');
        }
    }

    // 渲染活动结构
    function renderActivityStructure(activityStruct) {
        const container = document.getElementById('progress-container');
        document.title = `${activityStruct.displayName}-进度预览`;
        document.getElementById('headerTitle').textContent = `${activityStruct.displayName}-进度预览`;

        activityStruct.activityBlocks.forEach(block => {
            const wrapper = createBlockWrapper(block);
            container.appendChild(wrapper);
        });
    }

    // 创建区块容器
    function createBlockWrapper(block) {
        const wrapper = document.createElement('div');
        wrapper.className = block.type === 'SERIAL' ? 'main-flow' : 'parallel-section';

        if (block.type === 'PARALLEL') {
            if (!Array.isArray(block.list[0])) {
                console.error('PARALLEL 结构异常:', block);
                return wrapper;
            }
            wrapper.className = 'flex-row';
            block.list.forEach(parallelList => {
                const column = document.createElement('div');
                column.className = 'parallel-column flex-column';

                parallelList.forEach(activity => {
                        column.appendChild(createNodeElement(activity));
                });

                wrapper.appendChild(column);
            });
        } else {
            wrapper.className = 'flex-column';
            block.list.forEach(activity => {
                wrapper.appendChild(createNodeElement(activity));
            });
        }

        return wrapper;
    }

    // 创建节点元素
    function createNodeElement(activity) {
        const node = document.createElement('div');
        node.className = 'node status-waiting';
        node.innerHTML = `
                <div style="display: flex; align-items: center; margin-bottom: 0.5rem;">
                    <span class="node-icon">${activity.icon}</span>
                    <span style="margin-left: 0.5rem; font-weight: 500;">${activity.title}</span>
                </div>
                <span class="status-badge">等待开始</span>
                <div class="progress-container" style="display:none">
                    <div class="progress-bar" style="width: 1%"></div>
                </div>
            `;
        node.id = activity.id;
        return node;
    }

    function validateResponse(resp) {
        if (resp.code !=='0') {
            console.error('无效的 API 响应结构:', resp);
            throw new Error('无效的 API 响应结构');
        }
        return resp.data;
    }

    // 初始化流程
    async function init() {
        if (!activityId) {
            alert('未指定 activityId');
            return;
        }
        if(progressId === '_shoulderMockAndTest') {
            // 演示模式：执行mock
            mockProgress();
        }
        try {
            const activityStruct = validateResponse(await fetchActivityStruct());
            renderActivityStructure(activityStruct);

            if (!progressId) {
                alert('未指定 progressId');
                return;
            }

            startProgressPolling(activityStruct.endStepId);
        } catch (error) {
            console.error('初始化失败:', error);
        }
    }

    // 启动进度轮询
    function startProgressPolling(endStepId) {
        const intervalId = setInterval(async () => {
            try {
                const resp = await fetch(
                    `http://localhost:8080/api/v1/activities/progress?progressId=${progressId}&activityId=${activityId}`
                );
                const data = validateResponse(await resp.json());

                if (updateProgress(data, endStepId, intervalId)) {
                    clearInterval(intervalId);
                }
            } catch (error) {
                clearInterval(intervalId);
            }
        }, 500);
    }


    function updateProgress(data, endStepId, intervalId) {
        let allCompleted = true;
        Object.entries(data).forEach(([taskKey, taskData]) => {
            const node = document.getElementById(taskKey);
            if (!node) return;

            const badge = node.querySelector('.status-badge');
            const progressBarContainer = node.querySelector('.progress-container');
            const progressBar = node.querySelector('.progress-bar');

            // 状态处理逻辑
            let costText = convertCostToText(taskData.timeConsumed);
            let leftTimeText = convertCostToText(taskData.timeLeft);
            switch (taskData.status) {
                case 0: // WAITING
                    node.className = 'node status-waiting';
                    badge.textContent = '等待开始';
                    if (progressBarContainer) {
                        progressBarContainer.style.display = 'none';
                    }
                    break;
                case 1: // RUNNING
                    node.className = 'node status-running';
                    badge.textContent = `${Math.round(taskData.processed / taskData.totalNum * 100)}%，预计还需 ${leftTimeText}`;
                    if (progressBarContainer) {
                        progressBarContainer.style.display = 'block';
                    }
                    if (progressBar) {
                        progressBar.style.width = `${Math.round(taskData.processed / taskData.totalNum * 100)}%`;
                    }
                    break;
                case 2: // EXCEPTION
                    node.className = 'node status-exception';
                    badge.textContent = `异常终止，耗时 ${costText}`;
                    alert('执行异常，请检查日志！异常节点：' + taskKey);
                    clearInterval(intervalId);
                    break;
                case 3: // FINISHED
                    if (node.id === endStepId) {
                        // 最后一个任务完成，停止定时查询
                        clearInterval(intervalId);
                        triggerConfetti();
                    }
                    node.className = 'node status-completed';
                    badge.textContent = `✅ 已完成，耗时 ${costText}`;
                    if (progressBarContainer) {
                        progressBarContainer.style.display = 'none';
                    }
                    break;
                default:
                    badge.textContent = '未知状态';
                    if (progressBarContainer) {
                        progressBarContainer.style.display = 'none';
                    }
            }

            if (taskData.status !== 3) allCompleted = false;
        });

        return allCompleted;

    }

    function triggerConfetti() {
        const confettiCount = 100;
        for (let i = 0; i < confettiCount; i++) {
            const confetti = document.createElement('div');
            confetti.classList.add('confetti');
            confetti.style.left = `${Math.random() * 100}vw`;
            confetti.style.animationDelay = `${Math.random() * 2}s`;
            confetti.style.backgroundColor = `hsl(${Math.random() * 360}, 100%, 50%)`;
            document.body.appendChild(confetti);

            confetti.addEventListener('animationend', () => {
                confetti.remove();
            });
        }
    }

    function convertCostToText(cost) {
        if (cost <= 20) {
            return '一眨眼';
        } else if (cost > 20 && cost <= 1000) {
            return `${cost.toFixed(0)} ms`;
        } else if (cost > 1000 && cost <= 100000) {
            return `${(cost / 1000).toFixed(1)} s`;
        } else {
            return '未知';
        }
    }

</script>
<script src="https://cdn.bootcdn.net/ajax/libs/twitter-bootstrap/5.3.0/js/bootstrap.bundle.min.js"></script>
</body>
</html>